package com.stress.testing.agent.resolver;

import com.stress.testing.agent.Logger;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DefaultAgentResourceResolver implements AgentResourceResolver {
    private static Logger logger = Logger.getLogger(DefaultAgentResourceResolver.class);

    private Pattern agentJarPattern;

    private final String runningClassPath;

    private String agentWorkspace;
    private String agentJarName;
    private String agentJarFullName;

    private String extendLibPath;
    private List<URL> extendLibURL;

    private String agentDependencePath;
    private List<URL> agentDependenceURL;

    private String agentPluginPath;
    private Map<String, List<URL>> agentPluginURL;

    private String agentConfigFullName;

    private AtomicBoolean resolved;

    public DefaultAgentResourceResolver() {
        this(System.getProperty("java.class.path"));
    }

    public DefaultAgentResourceResolver(String runningClassPath) {
        this.resolved = new AtomicBoolean(false);
        this.runningClassPath = runningClassPath;
        this.agentJarPattern = DEFAULT_AGENT_JAR_PATTERN;
    }

    @Override
    public boolean resolve() {
        if (!resolved.compareAndSet(false, true)) {
            return false;
        }

        boolean resolveAgentJarResult = resolveAgentJar();
        if (!resolveAgentJarResult) {
            return false;
        }

        extendLibPath = agentWorkspace + File.separator + DEFAULT_EXTEND_LIB_DIRECTORY_NAME;
        extendLibURL = resolveExtendLibURL(extendLibPath);

        agentDependencePath = agentWorkspace + File.separator + DEFAULT_AGENT_DEPENDENCE_DIRECTORY_NAME;
        agentDependenceURL = resolveAgentDependenceURL(agentDependencePath);

        agentPluginPath = agentWorkspace + File.separator + DEFAULT_PLUGIN_DIRECTORY_NAME;
        agentPluginURL = resolveAgentPluginURL(agentPluginPath);

        agentConfigFullName = agentWorkspace + File.separator + DEFAULT_CONFIG_FILE_NAME;

        return true;
    }

    private boolean resolveAgentJar() {
        Matcher matcher = agentJarPattern.matcher(runningClassPath);

        if (!matcher.find()) {
            return false;
        }

        agentJarName = resolveAgentJarName(matcher);

        agentJarFullName = resolveAgentJarFullName(runningClassPath, agentJarName);
        if (Objects.isNull(agentJarFullName) || agentJarFullName.length() <= 0) {
            return false;
        }

        agentWorkspace = resolveAgentWorkspace(agentJarFullName);
        if (Objects.isNull(agentWorkspace) || agentWorkspace.length() <= 0) {
            return false;
        }

        try {
            agentWorkspace = new File(agentWorkspace).getCanonicalPath();
        } catch (IOException e) {
            logger.error("将agent工作目录解析为标准目录时出错", e);
        }


        return true;
    }

    private String resolveAgentJarName(Matcher matcher) {
        int begin = matcher.start();
        int end = matcher.end();
        return runningClassPath.substring(begin, end);
    }

    private String resolveAgentJarFullName(String runningClassPath, String agentJarName) {
        String[] classpaths = runningClassPath.split(File.pathSeparator);

        for (String classpath : classpaths) {
            if (classpath.contains(agentJarName)) {
                return classpath;
            }
        }

        return null;
    }

    private String resolveAgentWorkspace(String agentJarFullName) {
        int index1 = agentJarFullName.lastIndexOf("/");
        int index2 = agentJarFullName.lastIndexOf("\\");
        int index = Math.max(index1, index2);

        return index == -1 ? null : agentJarFullName.substring(0, index);
    }

    private List<URL> resolveExtendLibURL(String extendLibPath) {
        return resolveJarURL(extendLibPath);
    }

    private List<URL> resolveAgentDependenceURL(String agentDependencePath) {
        return resolveJarURL(agentDependencePath);
    }

    private Map<String, List<URL>> resolveAgentPluginURL(String agentPluginPath) {
        File pluginRootDir = new File(agentPluginPath);
        if (!pluginRootDir.exists()) {
            return null;
        }

        if (!pluginRootDir.isDirectory()) {
            return null;
        }

        File[] pluginDirs = pluginRootDir.listFiles(pathname -> pathname.isDirectory());

        if (Objects.isNull(pluginDirs) || pluginDirs.length <= 0) {
            return null;
        }

        Map<String, List<URL>> agentPluginURL = new HashMap<>();
        for (File pluginDir : pluginDirs) {
            List<URL> urls = resolveJarURL(pluginDir);
            if (urls != null && urls.size() > 0) {
                agentPluginURL.put(pluginDir.getName(), urls);
            }
        }

        return agentPluginURL;
    }

    private List<URL> resolveJarURL(String path) {
        File jarDir = new File(path);
        return resolveJarURL(jarDir);
    }

    private List<URL> resolveJarURL(File dir) {
        if (!dir.exists()) {
            return null;
        }

        if (!dir.isDirectory()) {
            return null;
        }

        File[] jars = dir.listFiles(pathname -> pathname.getName().endsWith(EXTENSION_SEPARATOR + JAR_FILE_EXTENSION));

        if (Objects.isNull(jars) || jars.length <= 0) {
            return null;
        }

        List<URL> urls = new ArrayList<>(jars.length);
        for (File jar : jars) {
            try {
                urls.add(jar.toURI().toURL());
            } catch (MalformedURLException e) {
                logger.error("添加URL失败", e);
            }
        }

        return urls;
    }

    @Override
    public String getAgentWorkspace() {
        return agentWorkspace;
    }

    @Override
    public String getAgentJarName() {
        return agentJarName;
    }

    @Override
    public String getAgentJarFullName() {
        return agentJarFullName;
    }

    @Override
    public String getExtendLibPath() {
        return extendLibPath;
    }

    @Override
    public List<URL> getExtendLibURL() {
        if (extendLibURL == null) {
            return null;
        }

        return Collections.unmodifiableList(extendLibURL);
    }

    @Override
    public String getAgentDependencePath() {
        return agentDependencePath;
    }

    @Override
    public List<URL> getAgentDependenceURL() {
        if (agentDependenceURL == null) {
            return null;
        }

        return Collections.unmodifiableList(agentDependenceURL);
    }

    @Override
    public String getAgentPluginPath() {
        return agentPluginPath;
    }

    @Override
    public Map<String, List<URL>> getAgentPluginURL() {
        return Collections.unmodifiableMap(agentPluginURL);
    }

    @Override
    public String getAgentConfigFullName() {
        return agentConfigFullName;
    }
}
